package com.wuhuahe.learning.lagou.cloud.gateway.filters;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.wuhuahe.learning.lagou.cloud.gateway.config.GatewayConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

//@Component
public class RegisterFilter implements GlobalFilter, Ordered {

    @Autowired
    private GatewayConfig gatewayConfig;

    final private Map<String, Cache<Long, Long>> registerReqIpCounts = new ConcurrentHashMap<>();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        URI uri = request.getURI();
        String path = uri.getPath();
        ServerHttpRequest.Builder mutate = request.mutate();
        if (path.startsWith("/api/user/register")) {  // 只拦截注册请求
            int latestMinutes = gatewayConfig.getLatestMinutes();
            int maxCounts = gatewayConfig.getMaxCounts();
            String ip = getIpAddress(request);
            Cache<Long, Long> ipCounts = registerReqIpCounts.getOrDefault(ip,
                    CacheBuilder.newBuilder().expireAfterWrite(latestMinutes, TimeUnit.MINUTES).build());
            int counts
                    = ipCounts.asMap().values().size();
            System.err.println("IP: " + ip + ",在" + latestMinutes + "分钟内连续注册： " + counts + "次！");
            ipCounts.put(System.currentTimeMillis(), 1L);
            registerReqIpCounts.put(ip, ipCounts);
            if (counts + 1 >= maxCounts) {
                response.setStatusCode(HttpStatus.FORBIDDEN);
                DataBuffer wrap = response.bufferFactory().wrap(
                        ("Sign up too often! Please wait for " + latestMinutes + " minutes and retry!").getBytes());
                return response.writeWith(Mono.just(wrap));
            }
        }
        ServerHttpRequest buildReuqest = mutate.build();
        return chain.filter(exchange.mutate().request(buildReuqest).build());
    }

    @Override
    public int getOrder() {
        return 1;
    }


    /**
     * 获取真正的IP
     * @param request
     * @return
     * @throws IOException
     */
    private String getIpAddress(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String ip = headers.getFirst("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值，第一个ip才是真实ip
            if (ip.contains(",")) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddress().getAddress().getHostAddress();
        }
        return ip;
    }
}
